package com.tianji.learning.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.pojo.dto.LearningPlanDTO;
import com.tianji.learning.pojo.po.LearningLesson;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.pojo.po.LearningRecord;
import com.tianji.learning.pojo.vo.LearningLessonVO;
import com.tianji.learning.pojo.vo.LearningPlanPageVO;
import com.tianji.learning.pojo.vo.LearningPlanVO;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 学生课程表 服务实现类
 *
 * @author author
 * @since 2023-11-01
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper recordMapper;

    /**
     * 下单后将课程信息保存到课程表中
     *
     */
    @Override
    @Transactional
    public void addUserLessons(Long userId, List<Long> courseIds) {
        // 根据课程id集合查询课程简单信息
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollectionUtil.isEmpty(cInfoList)) {
            log.debug("没有查询到课程信息,课程ID：{}",courseIds);
            return;
        }

        // 遍历课表数据，封装课程表数据保存
        List<LearningLesson> learningLessonList = new ArrayList<>(cInfoList.size());
        for (CourseSimpleInfoDTO cInfo : cInfoList) {
            // 当课程有效期不存在或有效时间小于等于0不进行保存
            if (ObjectUtil.isEmpty(cInfo.getValidDuration()) || cInfo.getValidDuration() <= 0) {
                continue;
            }
            LearningLesson lesson = new LearningLesson();
            lesson.setUserId(userId);
            lesson.setCourseId(cInfo.getId());
            lesson.setExpireTime(LocalDateTimeUtil.now().plusMonths(cInfo.getValidDuration()));
            learningLessonList.add(lesson);
        }

        // 批量新增课程数据到课程表
        this.saveBatch(learningLessonList);
    }


    /**
     * 分页查询课程表信息
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        Long userId = UserContext.getUser();
        // 通过已封装方法设置分页信息、默认以最近学习时间排序
        // select * from learning_lesson where user_id = #{userId} order by latest_learn_time limit 0, 5
        Page<LearningLesson> page = lambdaQuery().eq(LearningLesson::getUserId, userId)
                .page(query.toMpPage("latest_learn_time", false));

        // 没有查询到数据返回空的page对象
        if (ObjectUtil.isEmpty(page) || CollectionUtil.isEmpty(page.getRecords())) {
            return PageDTO.<LearningLessonVO>empty(page);
        }

        // 一次性查询，避免多次数据库交互
        List<LearningLesson> lessonList = page.getRecords();
        List<Long> courseIds = lessonList.stream()
                .map(LearningLesson::getCourseId).collect(Collectors.toList());

        // list转map，通过id获取对象
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollectionUtil.isEmpty(simpleInfoList)) {
            throw new BadRequestException("课程信息不存在！");
        }
        Map<Long, CourseSimpleInfoDTO> csInfoMap = simpleInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));

        // 构造VO对象
        List<LearningLessonVO> voList = new ArrayList<>(lessonList.size());
        for (LearningLesson lesson : lessonList) {
            LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);

            CourseSimpleInfoDTO cfInfo = csInfoMap.get(lesson.getCourseId());
            vo.setCourseName(cfInfo.getName());
            vo.setCourseCoverUrl(cfInfo.getCoverUrl());
            vo.setSections(cfInfo.getSectionNum());
            voList.add(vo);
        }
        return PageDTO.of(page, voList);
    }

    /**
     * 查询正在学习的课程
     */
    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        Long userId = UserContext.getUser();
        // 查询当前用户下正在学习的课程信息，以最近学习时间排序
        // 查询正在学习的课程 select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        List<LearningLesson> lessonList = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .list();

        if (CollectionUtil.isEmpty(lessonList)) {
            return null;
        }

        LearningLesson lesson = lessonList.get(0);
        CourseFullInfoDTO cfInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (ObjectUtil.isEmpty(cfInfo)) {
            throw new BadRequestException("课程不存在");
        }

        LearningLessonVO vo = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        vo.setCourseName(cfInfo.getName());
        vo.setCourseCoverUrl(cfInfo.getCoverUrl());
        vo.setSections(cfInfo.getSectionNum());

        // 统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
        Integer courseAmount = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();
        vo.setCourseAmount(courseAmount);

        // 查询小节信息
        List<CataSimpleInfoDTO> sInfoDTOList = catalogueClient.batchQueryCatalogue(Collections.singleton(lesson.getLatestSectionId()));
        if (CollectionUtil.isNotEmpty(sInfoDTOList)) {
            CataSimpleInfoDTO simpleInfoDTO = sInfoDTOList.get(0);
            vo.setLatestSectionName(simpleInfoDTO.getName());
            vo.setLatestSectionIndex(simpleInfoDTO.getCIndex());
        }
        return vo;
    }

    /**
     * 根据课程id，查询当前用户的课表中是否有该课程，如果有该课程则需要返回课程的学习进度、课程有效期等信息
     */
    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        Long userId = UserContext.getUser();

        // 通过用户ID、课程ID查询课表信息
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();

        // 通过learningLesson封装VO对象
/*        LearningLessonVO vo = new LearningLessonVO();
        if (ObjectUtil.isNotEmpty(lesson)) {
            vo.setCourseId(courseId);
            vo.setStatus(LessonStatus.of(lesson.getStatus()));
            vo.setLearnedSections(lesson.getLearnedSections());
            vo.setCreateTime(lesson.getCreateTime());
            vo.setExpireTime(lesson.getExpireTime());
        }*/
        return BeanUtils.copyBean(lesson, LearningLessonVO.class);
    }

    /**
     * 用户退款后通过MQ异步删除课程信息
     *
     */
    @Override
    public void deleteCourseFromLesson(Long userId, Long courseId) {
        remove(new LambdaQueryWrapper<LearningLesson>()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId,courseId));
    }

    /**
     * 校验当前用户是否可以学习当前课程
     *
     * @param courseId 课程id
     * @return lessonId，如果是报名了则返回lessonId，否则返回空
     */
    @Override
    public Long isLessonValid(Long courseId) {
        Long userId = UserContext.getUser();

        // 通过用户ID、课程ID查询课表信息
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .one();

        if (ObjectUtil.isNotEmpty(lesson) && !lesson.getStatus().equals(LessonStatus.EXPIRED.getValue())) {
            return lesson.getId();
        }
        return null;
    }

    /**
     * 9、创建学习计划
     *
     * @param planDTO
     */
    @Override
    public void createPlans(LearningPlanDTO planDTO) {
        LearningLesson lesson = getLearningLesson(planDTO.getCourseId());
        AssertUtils.isNotNull(lesson, "课程信息不存在！");

        boolean isUpdate = lambdaUpdate().set(LearningLesson::getWeekFreq, planDTO.getFreq())
                .set(lesson.getPlanStatus().getValue() == PlanStatus.NO_PLAN.getValue(), LearningLesson::getPlanStatus,
                        PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId, lesson.getId())
                .update();
        AssertUtils.isTrue(isUpdate, "更新学习计划失败！");
    }

    /**
     * 统计课程学习人数
     *
     * @param courseId 课程id
     * @return 学习人数
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        List<LearningLesson> lessonList = lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING, LessonStatus.LEARNING)
                .list();
        return ObjectUtil.isEmpty(lessonList) ? 0 : lessonList.size();
    }

    /**
     * 通过用户和课程id查询课表信息
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLesson getLearningLesson(Long courseId) {
        return lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getCourseId, courseId)
                .one();
    }

    /**
     * 查询我的学习计划
     *
     * @param query
     */
    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        LearningPlanPageVO result = new LearningPlanPageVO();
        Long userId = UserContext.getUser();

        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());

        // 本周课表信息
        Page<LearningLesson> pageResult = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .between(LearningLesson::getLatestLearnTime, begin, end)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = pageResult.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            result.emptyPage(pageResult);
        }

        // 统计学习记录中本周学习小节数
        QueryWrapper<LearningRecord> wrapper = Wrappers.<LearningRecord>query().eq("user_id", userId)
                .eq("finished", true)
                .between("finish_time", begin, end)
                .select("lesson_id as id,count(1) as sum")
                .groupBy("lesson_id");
        // { id:{}  sum:{} },{ id:{}  sum:{} }
        List<Map<String, Object>> mapList = recordMapper.selectMaps(wrapper);

        // { {id}:{sum} },{ {id}:{sum} }
        Map<Long, Integer> collect = mapList.stream()
                .collect(Collectors.toMap(c -> (Long) c.get("id"), c -> Integer.valueOf(c.get("sum").toString())));

        // 查询课程数据
        Map<Long, CourseSimpleInfoDTO> cInfoMap = getCInfoMap(records);
        ArrayList<LearningPlanVO> voList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(record, LearningPlanVO.class);
            // 获取课程详细信息
            CourseSimpleInfoDTO simpleInfoDTO = cInfoMap.get(record.getCourseId());
            if (ObjectUtil.isNotEmpty(simpleInfoDTO)) {
                learningPlanVO.setCourseName(simpleInfoDTO.getName());
                learningPlanVO.setSections(simpleInfoDTO.getSectionNum());
            }

            // 本周已学习小节数
            learningPlanVO.setWeekLearnedSections(collect.getOrDefault(learningPlanVO.getId(), 0));
            voList.add(learningPlanVO);
        }

        // 本周计划总小节数
        int planSum = voList.stream().mapToInt(LearningPlanVO::getWeekFreq).sum();
        // 本周已学习总小节数
        int sum = voList.stream().mapToInt(LearningPlanVO::getWeekLearnedSections).sum();

        result.setWeekFinished(sum);
        result.setWeekTotalPlan(planSum);
        return result.pageInfo(pageResult.getTotal(), pageResult.getPages(), voList);
    }

    /**
     * 将LearningLesson list转为map
     *
     * @param records
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> getCInfoMap(List<LearningLesson> records) {
        //3.1、获取课程ID集合
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        //3.2、远程调用查询课程信息
        List<CourseSimpleInfoDTO> courseInfoList = courseClient.getSimpleInfoList(courseIds);
        if (ObjectUtil.isEmpty(courseInfoList)) {
            //课程都不存在
            throw new BadRequestException("课程信息不存在");
        }
        //3.3、将list转为map,便于下面的逻辑直接从map中根据courseId获取课程信息
        return courseInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
    }
}
